package com.hmall.search.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hmall.feign.clients.ItemClient;
import com.hmall.feign.dto.PageDTO;
import com.hmall.feign.pojo.Item;
import com.hmall.feign.pojo.ItemDoc;
import com.hmall.search.mapper.SearchMapper;
import com.hmall.search.pojo.RequestParams;
import com.hmall.search.service.ISearchService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.Operator;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.io.IOException;
import java.util.*;

@Slf4j
@Service
public class SearchService extends ServiceImpl<SearchMapper, Item> implements ISearchService {
    @Autowired
    private RestHighLevelClient client;
    @Autowired
    private ItemClient itemClient;

    public void importIndex(List<Item> list) {
        BulkRequest bulkRequest = new BulkRequest();
        for (Item item : list) {
            ItemDoc itemDoc = new ItemDoc(item);
            String jsonString = JSON.toJSONString(itemDoc);
            IndexRequest indexRequest = new IndexRequest("hmall").id(item.getId().toString());
            indexRequest.source(jsonString, XContentType.JSON);
            bulkRequest.add(indexRequest);
        }
        try {
            client.bulk(bulkRequest, RequestOptions.DEFAULT);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }


    @Override
    public PageDTO<ItemDoc> search(RequestParams params) {
        try {
            SearchRequest searchRequest = new SearchRequest("hmall");
            //调用方法

            creatSearchRequest(params, searchRequest);

            //分页
            int page = params.getPage();
            int size = params.getSize();
            searchRequest.source().from((page - 1) * size);
            searchRequest.source().size(size);

            //设置排序
            String sortBy = params.getSortBy();
            if (sortBy.equals("sold")) {
                // 销量排序
                searchRequest.source().sort(sortBy, SortOrder.DESC);
            } else if (sortBy.equals("price")) {
                // 价格排序
                searchRequest.source().sort(sortBy, SortOrder.ASC);
            }
            searchRequest.source().highlighter(new HighlightBuilder().field("name").requireFieldMatch(false));
            //发请求
            SearchResponse response = client.search(searchRequest, RequestOptions.DEFAULT);
            //解析结果
            return praseResult(response);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    @Override
    public void removeIndex(String itemId) {
        DeleteRequest deleteRequest = new DeleteRequest("hmall").id(itemId);
        try {
            client.delete(deleteRequest, RequestOptions.DEFAULT);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public void createIndex(String itemId) {
        Item item = itemClient.getById(Long.valueOf(itemId));

        IndexRequest indexRequest = new IndexRequest("hmall").id(item.getId().toString());
        ItemDoc itemDoc = new ItemDoc(item);
        indexRequest.source(JSON.toJSONString(itemDoc), XContentType.JSON);

        try {
            client.index(indexRequest, RequestOptions.DEFAULT);
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    private static PageDTO<ItemDoc> praseResult(SearchResponse response) {
        SearchHits searchHits = response.getHits();
        long total = searchHits.getTotalHits().value;
        SearchHit[] hits = searchHits.getHits();
        List<ItemDoc> itemDocs = new ArrayList<>();
        for (SearchHit hit : hits) {
            String jsonStr = hit.getSourceAsString();
            ItemDoc itemDoc = JSON.parseObject(jsonStr, ItemDoc.class);


            Map<String, HighlightField> highlightFields = hit.getHighlightFields();
            if (!CollectionUtils.isEmpty(highlightFields)) {
                HighlightField highlightField = highlightFields.get("name");
                if (highlightField != null) {
                    String newName = highlightField.getFragments()[0].toString();
                    itemDoc.setName(newName);
                }
            }


            itemDocs.add(itemDoc);
        }
        return new PageDTO<ItemDoc>(total, itemDocs);
    }

    private static void creatSearchRequest(RequestParams params, SearchRequest searchRequest) {
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        //关键字
        if (StringUtils.isBlank(params.getKey())) {
            boolQueryBuilder.must(QueryBuilders.matchAllQuery());
        } else {
            boolQueryBuilder.must(QueryBuilders.matchQuery("all", params.getKey()).operator(Operator.AND));
        }
        //分类
        if (StringUtils.isNotBlank(params.getCategory())) {
            boolQueryBuilder.filter(QueryBuilders.termQuery("category", params.getCategory()));
        }
        //品牌
        if (StringUtils.isNotBlank(params.getBrand())) {
            boolQueryBuilder.filter(QueryBuilders.termQuery("brand", params.getBrand()));
        }

        //价格
        if (params.getMaxPrice() != null && params.getMinPrice() != null) {
            boolQueryBuilder.filter(QueryBuilders.rangeQuery("price").gte(params.getMinPrice() * 100).lte(params.getMaxPrice() * 100));
        }


        searchRequest.source().query(boolQueryBuilder);
    }
}
